package com.motu.vertx.module.utility.model;

import com.motu.vertx.module.utility.base.ServerConfig;
import com.motu.vertx.module.utility.kafka.KafkaManager;
import com.motu.vertx.module.utility.mysql.MysqlClient;
import com.motu.vertx.module.utility.mysql.MysqlTool;
import com.motu.vertx.module.utility.redis.RedisTools;
import com.motu.vertx.module.utility.toolset.TimeUtils;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.kafka.client.producer.KafkaProducer;
import io.vertx.kafka.client.producer.KafkaProducerRecord;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Properties;

/**
 * 游戏数据
 */
public class DbManager {

	public final static Logger logger = LoggerFactory.getLogger(DbManager.class);
	public static io.vertx.kafka.client.producer.KafkaProducer<String, String> producer;// game库的生产者

	public static void init(Vertx vertx, JsonObject appConfig) {

		// 创建game kafka
		String address = appConfig.getString("kafka-address");
		if (!Tool.isEmpty(address)) {
			Properties config = new Properties();
			config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, address);
			config.put("key.serializer", StringSerializer.class);
			config.put("value.serializer", StringSerializer.class);
			config.put("acks", "1");
			// 优化kafka生产者配置
			KafkaManager.upgradeProducerConfig(config, appConfig);
			producer = KafkaProducer.create(vertx, config);
			logger.info("game data kafka create success address:{}", address);
		} else {
			logger.error("game data kafka create fail address is null!!!");
		}
	}

	/**
	 * 更新游戏数据
	 */
	public static void pushDBQueue(String table, String key, JsonObject params) {
		if (Tool.isEmpty(key)) {
			key = table;
		}
		KafkaProducerRecord<String, String> record = KafkaProducerRecord.create("db_game", key, params.toString());
		producer.send(record, done -> {
			if (done.failed()) {
				logger.error("DbManager pushDBQueue fail cause = " + Tool.getException(done.cause()));
			} else {
//                io.vertx.kafka.client.producer.RecordMetadata recordMetadata = done.result();
//                MainVerticle.logger.info("pushDBQueue Message " + record.value().toString() + " written on topic=" + recordMetadata.getTopic() +
//                        ", partition=" + recordMetadata.getPartition() +
//                        ", offset=" + recordMetadata.getOffset());
			}
		});
	}

	/**
	 * 立刻更新到数据库中
	 */
	public static void updateDbNow(JsonObject obj) {
		int status = obj.getInteger("status");// 0需要判断是否存在数据库，1新增，2修改，3删除
		String tableName = obj.getString("table");// 表名
		Long id = obj.getLong("id");
		String redisKey = obj.getString("key");//redis key
		JsonObject updateFields = obj.getJsonObject("update");// 修改值
		JsonObject addFields = obj.getJsonObject("add");// 增加值
		if (status == 0) {// 0需要判断是否存在数据库
			String sql = "select count(1) as count from " + tableName + " where id = ?;";
			JsonArray sqlParams = new JsonArray();
			sqlParams.add(id);
			MysqlTool.queryJsonObjectFromDb(MysqlClient.getGamePool(), sql, sqlParams, h -> {
				if (h.succeeded()) {
					JsonObject jsonObject = h.result();
					if (jsonObject.getLong("count") == 0) {// 不存在则新增
						insert(tableName, redisKey, updateFields);
					} else {// 存在则修改
						update(tableName, redisKey, id, updateFields, addFields);
					}
				} else {
					logger.error(Tool.getException(h.cause()));
				}
			});
		} else if (status == 1) {// 新增
			insert(tableName, redisKey, updateFields);
		} else if (status == 2) {// 修改
			update(tableName, redisKey, id, updateFields, addFields);
		} else if (status == 3) {// 删除
			if (obj.containsKey("sql")) {
				delete(obj.getString("sql"), obj.getString("params"));
			} else {
				delete(tableName, redisKey, id);
			}
		}
	}

	public static void insert(String tableName, String redisKey, JsonObject jsonObject) {
//        logger.info("insert db tableName:"+tableName+" redisKey:"+redisKey+" jsonObject:"+jsonObject.toString());
		String fields = "";
		String values = "";
		JsonArray params = new JsonArray();
		for (Iterator<Entry<String, Object>> iter1 = jsonObject.iterator(); iter1.hasNext(); ) {
			Entry<String, Object> entry1 = iter1.next();
			String field = EntityManager.isNewCode() ? Tool.upperCaseToUnderline(entry1.getKey()) : entry1.getKey();
			field = "`" + field + "`";// 字段名转义，避免关键字冲突
			Object val = entry1.getValue();
			if (val == null) {
				val = "";
			}
			if (fields.equals("")) {
				fields = field;
			} else {
				fields += "," + field;
			}
			if (values.equals("")) {
				values = "?";
			} else {
				values += ",?";
			}
			if (val != null) {
				params.add(val);
			} else {
				logger.error("insert db error tableName:" + tableName + " field:" + field + " redisKey:" + redisKey + " jsonObject:" + jsonObject.toString());
			}
		}
		String sql = "insert into " + tableName + "(" + fields + ") values(" + values + ");";
		MysqlTool.updateToDb(MysqlClient.getGamePool(), sql, params, handler -> {
			if (handler.failed()) {
				addErrorLog(tableName, redisKey, sql, params.toString(), handler.cause().getMessage());
			}
		});
	}

	public static void update(String tableName, String redisKey, long id, JsonObject updateObj, JsonObject addObj) {
		String fields = "";
		JsonArray params = new JsonArray();
		if (addObj != null) {
			for (Iterator<Entry<String, Object>> iter1 = addObj.iterator(); iter1.hasNext(); ) {
				Entry<String, Object> entry1 = iter1.next();
				String field = EntityManager.isNewCode() ? Tool.upperCaseToUnderline(entry1.getKey()) : entry1.getKey();
				field = "`" + field + "`";// 字段名转义，避免关键字冲突
				Object val = entry1.getValue();
				if (val == null) {
					val = "";
				}
				if (fields.equals("")) {
					fields = field + " = " + field + " + ?";
				} else {
					fields += "," + field + " = " + field + " + ?";
				}
				params.add(val);
			}
		}
		if (updateObj != null) {
			for (Iterator<Entry<String, Object>> iter1 = updateObj.iterator(); iter1.hasNext(); ) {
				Entry<String, Object> entry1 = iter1.next();
				String field = EntityManager.isNewCode() ? Tool.upperCaseToUnderline(entry1.getKey()) : entry1.getKey();
				field = "`" + field + "`";// 字段名转义，避免关键字冲突
				Object val = entry1.getValue();
				if (val == null) {
					val = "";
				}
				if (fields.equals("")) {
					fields = field + " = ?";
				} else {
					fields += "," + field + " = ?";
				}
				params.add(val);
			}
		}
		params.add(id);
		String sql = "update " + tableName + " set " + fields + " where id = ?;";
		MysqlTool.updateToDb(MysqlClient.getGamePool(), sql, params, handler -> {
			if (handler.failed()) {
				addErrorLog(tableName, redisKey, sql, params.toString(), handler.cause().getMessage());
			} else {
				// 数据如果不存在，则insert
				Integer updateCount = (Integer) handler.result();
				if (updateCount != null && updateCount.intValue() == 0) {
					RedisTools.getHashJsonObject(redisKey, res2 -> {
						if (res2.succeeded()) {
							JsonObject result = res2.result();
							if (result != null && result.containsKey("id")) {
								logger.info("redisKey = " + redisKey + " update no exist insert to db");
								insert(tableName, redisKey, BaseModel.removeTempkey(result));
							}
						}
					});
				}
			}
		});
	}

	public static void delete(String tableName, String redisKey, long id) {
		String sql = "delete from " + tableName + " where id = ?;";
		JsonArray params = new JsonArray();
		params.add(id);
		MysqlTool.updateToDb(MysqlClient.getGamePool(), sql, params, handler -> {
			if (handler.failed()) {
				addErrorLog(tableName, redisKey, sql, params.toString(), handler.cause().getMessage());
			}
		});
	}

	public static void delete(String sql, String sqlParams) {
		JsonArray params = new JsonArray(sqlParams);
		MysqlTool.updateToDb(MysqlClient.getGamePool(), sql, params, handler -> {
			if (handler.failed()) {
				addErrorLog("", "", sql, params.toString(), handler.cause().getMessage());
			}
		});
	}

	private static void addErrorLog(String tableName, String redisKey, String errsql, String errparams, String errdesc) {
		String sql = "insert into redisdblog(tablename,rediskey,errsql,errparams,errdesc,time) values(?,?,?,?,?,?);";
		JsonArray params = new JsonArray();
		params.add(tableName);
		params.add(redisKey);
		params.add(errsql);
		params.add(errparams);
		params.add(errdesc);
		params.add(TimeUtils.getCurTime());
		MysqlTool.updateToDb(MysqlClient.getServerPool(), sql, params, handler -> {
			if (handler.failed()) {
				logger.error("add redis db log error sql = " + sql);
				logger.error("add redis db log error params = " + params.toString());
				logger.error("add redis db log error cause = " + handler.cause().getMessage());
			}
		});
	}

	/**
	 * 用户离线更新未入库队列
	 */
	public static void userOfflineUpdateDb(JsonObject data) {
		long userId = data.getLong("userid");
		String symbol = ";";
		RedisTools.getFullSet(EntityManager.getUserUpdateDbListKey(userId), res -> {
			if (res.succeeded()) {
				JsonArray jsonArray = res.result();
				if (jsonArray != null && !jsonArray.isEmpty()) {
					int size = jsonArray.size();
					logger.info("userOfflineUpdateDb userid = " + userId + " list size = " + size);
					for (int i = 0; i < size; i++) {
						String value = jsonArray.getString(i);

						try {
							String key = null;
							String listKeyField = null;
							String listKey = null;
							String listItemKey = null;

							String[] array = value.split(symbol);
							if (array.length == 1) {
								key = array[0];
							} else {
								key = array[0];
								listKeyField = array[1];
								listKey = array[2];
								listItemKey = array[3];
							}

							String _key = key;
							String _listKeyField = listKeyField;
							String _listKey = listKey;
							String _listItemKey = listItemKey;

							if (Tool.isEmpty(listKey)) {
								// 更新单条数据
								RedisTools.getHashJsonObject(_key, res2 -> {
									if (res2.succeeded()) {
										JsonObject result = res2.result();
										if (result != null && result.containsKey("id")) {
											updateDb(_key, null, result);
										}
									}
								});
							} else {
								// 更新列表数据
								RedisTools.getHashField(_listKey, _listItemKey, res2 -> {
									if (res2.succeeded()) {
										String result = res2.result();
										if (!Tool.isEmpty(result)) {
											JsonObject jsonObject = new JsonObject(result);
											if (jsonObject.containsKey("id")) {
												updateDb(_key, _listKeyField, jsonObject);
											}
										}
									}
								});
							}
						} catch (Exception e) {
							logger.error("userOfflineUpdateDb error value = {} cause = {}", value, Tool.getException(e));
						}

					}

					// 删除队列信息
					RedisTools.del(EntityManager.getUserUpdateDbListKey(userId));
				}
			} else {
				logger.error("userOfflineUpdateDb get user db list error userid = " + userId);
			}
		});
	}

	// 更新到数据库
	private static void updateDb(String key, String listKeyField, JsonObject updateObj) {
		String entityKey = key.split("\\.")[0];// 新规范类名，旧规范表名
		String tableName = EntityManager.isNewCode() ? Tool.upperCaseToUnderline(entityKey) : entityKey;
		JsonObject updateDb = new JsonObject();
		updateDb.put("status", BaseModel.UPDATE);// 0需要判断是否存在数据库，1新增，2修改，3删除
		updateDb.put("table", tableName);
		updateDb.put("id", getJsonObjectId(updateObj));
		updateDb.put("key", key);
		if (!Tool.isEmpty(listKeyField) && listKeyField.contains(".")) {// listkey有多个字段
			updateDb.put("listkey", listKeyField);
		}
		BaseModel.removeTempkey(updateObj);// 删除临时字段，不然会把临时字段更新到数据库中
		updateDb.put("update", updateObj);
//		if (ServerConfig.isReviewServer()) {// 评审服用redis队列写入数据库
//			RedisTools.pushToList(EntityManager.getUpdateDbKey(), updateDb.toString());
//		} else {
			DbManager.pushDBQueue(tableName, key, updateDb);
//		}
	}

	/**
	 * 获取对象里的id
	 */
	public static long getJsonObjectId(JsonObject jsonObject) {
		Object value = jsonObject.getValue("id");
		if (value instanceof CharSequence) {
			CharSequence cs = (CharSequence) value;
			return cs == null ? 0 : Long.parseLong(cs.toString());
		} else if (value instanceof Number) {
			Number number = (Number) value;
			if (number == null) {
				return 0;
			} else if (number instanceof Long) {
				return (Long) number;  // Avoids unnecessary unbox/box
			} else {
				return number.longValue();
			}
		} else {
			return 0;
		}
	}

}
